<?php
/**
* Mapping for a collection of related models using association table
* 
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* 
* @copyright Copyright (c) 2008-2009 Ne8, LLC <josh.ribakoff@gmail.com>
* @license New BSD License
* @version 0.1
* @category Shuffler
* @package Shuffler_Mapper
* @subpackage Mapping
*/
class Shuffler_Mapper_Mapping_Association extends Shuffler_Mapper_Mapping
{
    
    protected $associationTable;
    protected $mutatorMethod;
    protected $accessorMethod;
    
    /**
    * Setter strategy for Shuffler_Mapper->doLoad()
    * 
    * @param Shuffler_Model to inject data into
    * @return Shuffler_Model after injecting data
    */
    protected function doLoad( Shuffler_Model $model, array $rs )
    {
        $joinTable = strtolower( $this->class );
        $db = $this->getReadAdapter();
        $assocMapper = $this->getTargetMapper();
        $select = $db->select( 'a.*' )
            ->from( array( 'a' => $joinTable ), $assocMapper->getSelectCols() )
            ->join( array( 'b' => $this->getAssocTableName() ),  'b.' . $this->getJoinFk() . ' = a.id', array() )
            ->where( 'b.' . $this->getAssocFk() . ' = ?', (int)$model->getId() );
        $rs = $assocMapper->query( $select );
        
        foreach( $rs as $value )
        {
            $command = $this->getMutatorMethod();
            if( !method_exists( $model, $command ) )
            {
                throw new Exception( 'Method does not exist ' . get_class( $model ) . '->' . $command );
            }    
            $model->$command( $value );
        }           
        return $model; 
    }
    
    public function getJoinFk()
    {
        return strtolower( $this->class ) . '_id';
    }
    
    public function getAssocFk()
    {
        return $this->getField();
    }
    
    /**
    * @todo write tests
    */
    public function save( Shuffler_Model $model )
    {       
        try           
        {
            $db = $this->getWriteAdapter(); 
            // delete the association rows
            $db->delete( $this->getAssocTableName(), $this->getAssocFk() . ' = ' . (int)$model->getId() ); 
            $method = $this->getAccessorMethod();
            // and re-insert
            $collection = $model->$method();
            foreach( $collection as $subModel )
            {       
                $values = array( $this->getJoinFk() => $subModel->getId(), $this->getAssocFk() => $model->getId() );
                $db->insert( $this->getAssocTableName(), $values );        
            }
        }
        catch( Exception $e )
        {
            throw new Exception( 'Exception occured trying to save mapping ' . $e->getMessage() );
        }
    }
    
    protected function getAssocTableName()
    {
        if( !is_null( $this->associationTable ) )
        {
            return $this->associationTable;
        }
        return $this->sourceMapper->getTableName() . '_' . strtolower( $this->class );
    }
    
    /**
    * Get the name of the mutator ( "add" method )
    * 
    * @return string name of method for use in reflective calls
    * 
    * @todo pull up into mapping class & sibling classes
    */
    public function getMutatorMethod()
    {
        if( isset( $this->mutatorMethod ))
        {
            return $this->mutatorMethod;
        }
        return 'add' . $this->inflector->letterCaseToUnderscore( $this->getProperty() );
    }
    
    public function setMutatorMethod( $method )
    {
        $this->mutatorMethod = $method;
    }
    
    public function getAccessorMethod()
    {
        if( isset( $this->accessorMethod ))
        {
            return $this->accessorMethod;
        }
        return $command = 'get' . $this->getProperty() . 's';
    }
    
    public function setAccessorMethod( $method )
    {
        $this->accessorMethod = $method;
    }
    
    public function getType()
    {
        return 'association';   
    }
}